package com.sm.chatinputlibrary.emoji;

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.Spannable;
import android.text.TextUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 需要在Application里面或者在使用前进行初始化，获取表情
 * <p>
 * 表情文件命名为emoji.txt放在assets根目录下，格式为，表情码,文件名,[别名]
 * 表情码可以为空，其它2个不能为空
 * </p>
 * 参考{link https://github.com/vanniktech/Emoji/blob/master/emoji/src/main/java/com/vanniktech/emoji/EmojiManager.java}
 */
public class EmojiManager {
    private static EmojiManager INSTANCE;
    private final  String EMOJI_PATH = "emoji/%s.png";
    /**
     * 有表情码需要替换的表情
     */
    private Map<String, EmojiBean> emojiMap = new LinkedHashMap<>();
    /**
     * 所有的表情
     */
    private Map<String, EmojiBean> allEmojiMap = new LinkedHashMap<>();
    /**
     * 用户表情面板的所有表情
     */
    private List<EmojiBean> allEmoji;

    private Pattern emojiPattern;
    private Pattern allEmojiPattern;

    private EmojiManager(Context context) {
        install(context);
    }

    public static void init(@NonNull Context context) {
        if (INSTANCE == null) {
            synchronized (EmojiManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new EmojiManager(context);
                }
            }
        }
    }

    public static EmojiManager getInstance() {
        if (INSTANCE == null) {
            throw new IllegalStateException("EmojiManager 必须先调用init进行初始化加载表情资源");
        }
        return INSTANCE;
    }

    public void replaceWithImages(final Context context, final Spannable text, final float emojiSize) {
        final EmojiManager emojiManager = EmojiManager.getInstance();
        final EmojiSpan[] existingSpans = text.getSpans(0, text.length(), EmojiSpan.class);
        final List<Integer> existingSpanPositions = new ArrayList<>(existingSpans.length);

        for (EmojiSpan existingSpan : existingSpans) {
            existingSpanPositions.add(text.getSpanStart(existingSpan));
        }
        final List<EmojiRange> findAllEmojis = emojiManager.findAllEmojis(text);
        for (EmojiRange location : findAllEmojis) {
            if (!existingSpanPositions.contains(location.start)) {
                text.setSpan(new EmojiSpan(context, location.emoji, emojiSize),
                        location.start, location.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }

    @NonNull
    private List<EmojiRange> findAllEmojis(@Nullable final CharSequence text) {
        final List<EmojiRange> result = new ArrayList<>();
        if (!TextUtils.isEmpty(text)) {
            final Matcher matcher = emojiPattern.matcher(text);
            while (matcher.find()) {
                final EmojiBean found = findEmoji(text.subSequence(matcher.start(), matcher.end()));
                if (found != null) {
                    result.add(new EmojiRange(matcher.start(), matcher.end(), found));
                }
            }
            if (allEmojiPattern != null) {
                final Matcher allEmojiMatcher = allEmojiPattern.matcher(text);
                while (allEmojiMatcher.find()) {
                    final EmojiBean found = findEmojiForCustom(text.subSequence(allEmojiMatcher.start(), allEmojiMatcher.end()));
                    if (found != null) {
                        result.add(new EmojiRange(allEmojiMatcher.start(), allEmojiMatcher.end(), found));
                    }
                }
            }
        }
        return result;
    }

    @Nullable
    private EmojiBean findEmojiForCustom(@NonNull final CharSequence candidate) {
        return allEmojiMap.get(candidate.toString());
    }

    @Nullable
    private EmojiBean findEmoji(@NonNull final CharSequence candidate) {
        return emojiMap.get(candidate.toString());
    }

    /**
     * 获取所有表情
     */
    public List<EmojiBean> getAllEmoji() {
        if (allEmoji == null) {
            Collection<EmojiBean> valueCollection = allEmojiMap.values();
            allEmoji = new ArrayList<>(valueCollection);
        }
        return allEmoji;
    }

    /**
     * 加载需要过滤的表情，必须先调用这个
     * 表情这里分为2种
     * <ol>
     * <il>需要替换的表情，需要表情码</il>
     * <il>自己定义的表情，不需要表情码</il>
     * </ol>
     *
     * <p>
     * 格式:1F601,emoji_001,[微笑]
     * </p>
     */
    private void install(@NonNull Context context) {
        emojiMap.clear();
        final List<String> unicodesForPattern = new ArrayList<>();
        final List<String> allEmojiForPattern = new ArrayList<>();
        try {
            InputStream is = context.getAssets().open("emoji.txt");
            Reader reader = new InputStreamReader(is);
            BufferedReader streamReader = new BufferedReader(reader);
            String str;
            while ((str = streamReader.readLine()) != null) {
                String[] emojiStr = str.split(",");
                if (TextUtils.isEmpty(emojiStr[0])) {//不需要转换的表情直接添加到所有里面
                    EmojiBean bean = new EmojiBean("", String.format(EMOJI_PATH,emojiStr[1].trim()), emojiStr[2].trim());
                    allEmojiMap.put(bean.getEmojiAlias(), bean);
                    allEmojiForPattern.add(bean.getEmojiAlias());
                } else {
                    EmojiBean bean = new EmojiBean(getUnicode(emojiStr[0].trim()), String.format(EMOJI_PATH,emojiStr[1].trim()), emojiStr[2].trim());
                    emojiMap.put(bean.getUnicode(), bean);
                    unicodesForPattern.add(bean.getUnicode());
                    allEmojiMap.put(bean.getEmojiAlias(), bean);
                    allEmojiForPattern.add(bean.getEmojiAlias());
                }
            }
            streamReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (unicodesForPattern.isEmpty()) {
            throw new IllegalArgumentException("Your EmojiProvider must at least have one category with at least one emoji.");
        }
        // 我们需要按长度对unicodes进行排序，以便最长的首先匹配。
        Collections.sort(unicodesForPattern, STRING_LENGTH_COMPARATOR);
        Collections.sort(allEmojiForPattern, STRING_LENGTH_COMPARATOR);

        final StringBuilder patternBuilder = new StringBuilder();

        for (String code : unicodesForPattern) {
            patternBuilder.append(Pattern.quote(code)).append('|');
        }
        final String regex = patternBuilder.deleteCharAt(patternBuilder.length() - 1).toString();
        emojiPattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        final StringBuilder customPatternBuilder = new StringBuilder();
        for (String code : allEmojiForPattern) {
            customPatternBuilder.append(Pattern.quote(code)).append('|');
        }
        //所有表情
        if (customPatternBuilder.length() > 0) {
            final String customRegex = customPatternBuilder.deleteCharAt(customPatternBuilder.length() - 1).toString();
            allEmojiPattern = Pattern.compile(customRegex, Pattern.CASE_INSENSITIVE);
        }
    }

    /**
     * 根据文件名获取资源ID
     *
     * @param imageName 文件名
     */
    private int getResource(Context context, String imageName) {
        return context.getResources().getIdentifier(imageName, "drawable", context.getPackageName());
    }

    /**
     * 获取emoji的unicode
     *
     * @param s 不需要0x 16进制前缀
     */
    private String getUnicode(String s) {
//        return new String(new int[]{0x1F601}, 0, 1);
        return new String(new int[]{Integer.parseInt(s, 16)}, 0, 1);
    }
    private final Comparator<String> STRING_LENGTH_COMPARATOR = new Comparator<String>() {
        @Override
        public int compare(final String first, final String second) {
            final int firstLength = first.length();
            final int secondLength = second.length();
            return Integer.compare(secondLength, firstLength);
        }
    };
}
